package com.syp.uuid.core.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.syp.uuid.core.dao.AppSequenceMapper;
import com.syp.uuid.core.domain.pojo.AppSequenceEntity;
import com.syp.uuid.core.service.SerialServiceI;

@Service("uuidService")
public class SerialServiceImpl implements SerialServiceI {

	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	private AppSequenceMapper appSequenceMapper;

	@Override
	public long get(final String key) {
		String serial = stringRedisTemplate.execute(new RedisCallback<String>() {
			@Override
			public String doInRedis(RedisConnection conn) throws DataAccessException {
				return stringRedisTemplate.opsForList().leftPop(key);
			}
		});
		return Long.valueOf(serial);
	}

	@Override
	public boolean put(final String name, final long size) {
		boolean success = false;
		long originalTmp;
		long seedTmp;
		while (true) {
			AppSequenceEntity as = this.appSequenceMapper.getByName(name);
			as.setOriginalVal(as.getCurrentVal());
			if (as.getCurrentVal() >= as.getMax()) {
				as.setCurrentVal(as.getMin() + as.getSeed() * size);
			} else {
				as.setCurrentVal(as.getCurrentVal() + as.getSeed() * size);
			}
			success = this.appSequenceMapper.safeUpdate(as);
			if (success) {
				originalTmp = as.getOriginalVal();
				seedTmp = as.getSeed();
				break;
			}
		}

		final long original = originalTmp;
		final long seed = seedTmp;
		long t1 = System.currentTimeMillis();
		success = stringRedisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection conn) throws DataAccessException {
				for (int i = 1; i <= size; i++) {
					long serial = original + i * seed;
					stringRedisTemplate.opsForList().rightPush(name, String.valueOf(serial));
				}

				return true;
			}
		});
		long t2 = System.currentTimeMillis();
		System.err.println("总耗时：" + (t2 - t1));
		System.err.println("平均耗时：" + (t2 - t1) / size);
		return true;
	}
	
	@Override
	public long getSequenceRestLength(final String name) {
		long t1 = System.currentTimeMillis();
		long rest = stringRedisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection conn) throws DataAccessException {
				return stringRedisTemplate.opsForList().size(name);
			}
		});
		long t2 = System.currentTimeMillis();
		System.err.println("总耗时：" + (t2 - t1));
		return rest;
	}

	@Override
	public long next(final String name) {
		return stringRedisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection conn) throws DataAccessException {
				return stringRedisTemplate.opsForValue().increment(name, 1);
			}
		});
	}
}
